//
// Created on 2025/4/14.
//
// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,
// please include "napi/native_api.h".
#include "encoder.h"
#include <thread>

std::mutex VideoEncoder::inQueueMutex;
std::mutex VideoEncoder::outQueueMutex;
std::queue<CodecBufferInfo> VideoEncoder::inQueue;
std::queue<CodecBufferInfo> VideoEncoder::outQueue;

VideoEncoder::VideoEncoder()
{

}

VideoEncoder::~VideoEncoder()
{
    OH_VideoEncoder_Destroy(encoder);
    encoder = nullptr;
}


void VideoEncoder::OnError(OH_AVCodec *codec, int32_t errorCode, void *userData) {
    // 回调的错误码由开发者判断处理。
    (void)codec;
    (void)errorCode;
    (void)userData;
    OH_LOG_ERROR(LOG_APP, "errorCode is {public}%d .", errorCode);
}

void VideoEncoder::OnStreamChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData) {
    // Buffer模式下，该回调函数无作用。
    (void)codec;
    (void)format;
    (void)userData;
}

void VideoEncoder::OnNeedInputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData) {
//     // 获取视频宽、高跨距。
//     if (isFirstFrame) {
//         OH_AVFormat *format = OH_VideoEncoder_GetInputDescription(codec);
//         OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_STRIDE, &widthStride);
//         OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_SLICE_HEIGHT, &heightStride);
//         OH_AVFormat_Destroy(format);
//         isFirstFrame = false;
//     }
    // 输入帧的数据buffer和对应的index送入inQueue队列。
    (void)codec;
    (void)userData;
    std::unique_lock<std::mutex> lock(inQueueMutex);
    inQueue.emplace(index, buffer);
}

void VideoEncoder::OnNewOutputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData) {
    // 获取视频帧的平均量化参数，平方误差
//     OH_AVFormat *format = OH_AVBuffer_GetParameter(buffer);
//     OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_ENCODER_QP_AVERAGE, &qpAverage);
//     OH_AVFormat_GetDoubleValue(format, OH_MD_KEY_VIDEO_ENCODER_MSE, &mseValue);
//     OH_AVFormat_Destroy(format);
    // 完成帧的数据buffer和对应的index送入outQueue队列
    (void)userData;
    std::unique_lock<std::mutex> lock(outQueueMutex);
    outQueue.emplace(index, buffer);
}


void VideoEncoder::EncodeInt(SampleInfo sampleInfo)
{
    sampleInfo_ = sampleInfo;
    //1.创建编码器 以h264为例
    encoder = OH_VideoEncoder_CreateByMime(OH_AVCODEC_MIMETYPE_VIDEO_AVC);
    OH_AVFormat* format = OH_AVFormat_Create();
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_WIDTH, sampleInfo_.videoWidth);
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_HEIGHT, sampleInfo_.videoHeight);
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_PIXEL_FORMAT, sampleInfo_.pixelFormat);
    int32_t ret = OH_VideoEncoder_Configure(encoder, format);
    if (ret != AV_ERR_OK) {
    // 异常处理。
        OH_LOG_INFO(LOG_APP, "encoder Create Failed .");
        OH_VideoEncoder_Destroy(encoder);
        encoder = nullptr;
        return;
    }
    OH_AVFormat_Destroy(format);
    
    OH_AVCodecCallback cb = {&OnError, &OnStreamChanged, &OnNeedInputBuffer, &OnNewOutputBuffer};
    ret = OH_VideoEncoder_RegisterCallback(encoder, cb, nullptr);
    if (ret != AV_ERR_OK) {
        // 异常处理。
        OH_LOG_INFO(LOG_APP, "RegisterCallback Failed .");
        OH_VideoEncoder_Destroy(encoder);
        encoder = nullptr;
        return;
    }
    OH_VideoEncoder_Prepare(encoder);

    // 配置待编码文件路径。
//     std::string_view inputFilePath = "/*yourpath*.yuv";
//     std::string_view outputFilePath = "/*yourpath*.h264";

    inputFile->open(sampleInfo_.inputPath.data(), std::ios::in | std::ios::binary);
    outputFile->open(sampleInfo_.outputPath.data(), std::ios::out | std::ios::binary | std::ios::ate);
// 启动编码器，开始编码。
    ret = OH_VideoEncoder_Start(encoder);
    if (ret != AV_ERR_OK) {
        // 异常处理。
        OH_LOG_INFO(LOG_APP, "Encoder Start Failed .");
        OH_VideoEncoder_Destroy(encoder);
        encoder = nullptr;
        return;
    }
    OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_STRIDE, &widthStride);
    OH_LOG_INFO(LOG_APP, "Encoder Start success .");
    
    inthread = std::make_unique<std::thread>(&VideoEncoder::InputThread, this);
    outthread = std::make_unique<std::thread>(&VideoEncoder::OutputThread, this);
    return;
}

void VideoEncoder::InputThread() {
    while (true) {
        std::unique_lock<std::mutex> lock(inQueueMutex);
        CodecBufferInfo bufferInfo = inQueue.front();
        inQueue.pop();
        lock.unlock();
        int32_t frameSize = sampleInfo_.videoWidth * sampleInfo_.videoHeight * 3 / 2; // NV12像素格式下，每帧数据大小的计算公式。
        if (widthStride == sampleInfo_.videoWidth) {
            inputFile->read(reinterpret_cast<char *>(OH_AVBuffer_GetAddr(bufferInfo.buffer)), frameSize);
        } else {
            // 宽高跨度对齐
            std::vector<uint8_t> buffer(frameSize);
            inputFile->read(reinterpret_cast<char *>(buffer.data()), frameSize);
            //WidthOffset(buffer, bufferInfo.buffer);
        }

    // 配置buffer info信息
        OH_AVCodecBufferAttr info;
        info.size = frameSize;
        info.offset = 0;
        info.pts = 0;
        if (inputFile->eof()) {//尾帧
            info.flags = AVCODEC_BUFFER_FLAGS_EOS;
            
        }
        int32_t ret = OH_AVBuffer_SetBufferAttr(bufferInfo.buffer, &info);
    // 配置buffer 随帧信息
    // 值由开发者决定
        int32_t isIFrame;
        OH_AVFormat *parameter = OH_AVBuffer_GetParameter(bufferInfo.buffer);
        OH_AVFormat_SetIntValue(parameter, OH_MD_KEY_REQUEST_I_FRAME, isIFrame);
        ret = OH_AVBuffer_SetParameter(bufferInfo.buffer, parameter);
        OH_AVFormat_Destroy(parameter);
    // 送入编码输入队列进行编码，index为对应输入队列的下标。
        ret = OH_VideoEncoder_PushInputBuffer(encoder, bufferInfo.index);
        if (ret != AV_ERR_OK) {
            // 异常处理。
            break;
        }
    }
}

void VideoEncoder::OutputThread() {
    while (true) {
        std::unique_lock<std::mutex> lock(outQueueMutex);
        CodecBufferInfo bufferInfo = outQueue.front();
        outQueue.pop();

// 获取编码后信息
        OH_AVCodecBufferAttr info;
        int32_t ret = OH_AVBuffer_GetBufferAttr(bufferInfo.buffer, &info);
        if (ret != AV_ERR_OK) {
            // 异常处理
        }
// 将编码完成帧数据buffer写入到对应输出文件中
        outputFile->write(reinterpret_cast<char *>(OH_AVBuffer_GetAddr(bufferInfo.buffer)), info.size);
// 释放已完成写入的数据，index为对应输出队列的下标
        ret = OH_VideoEncoder_FreeOutputBuffer(encoder, bufferInfo.index);
        if (ret != AV_ERR_OK) {
            // 异常处理
            OH_LOG_ERROR(LOG_APP, "outputFile failed .");
            break;
        }
        if (info.flags == AVCODEC_BUFFER_FLAGS_EOS) {
            OH_LOG_INFO(LOG_APP, "outputFile end .");
            break;
        }
    }
}

